﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Threading;
using System.ComponentModel;
using System.Drawing;

namespace Prodcrast
{
    public class Episode : IEquatable<Episode>
    {
        public string Title { get; private set; }
        public string MediaURL { get; private set; }
        public DateTime Date { get; private set; }
        public Feed Feed { get; private set; }

        public Image Icon { get { return Feed.Icon; } }

        public Episode This { get { return this; } }

        private FileInfo m_FileInfo = null;

        public enum EpisodeState
        {
            Queued,
            Downloading,
            Paused,
            Downloaded,
            Listened
        }

        public bool Listened
        {
            get { return m_State == EpisodeState.Listened; }
            set
            {
                if (value) 
                    m_State = EpisodeState.Listened;
                else
                {
                    m_State = EpisodeState.Queued;
                    CheckFile();
                }
            }
        }

        public bool NeedsRefresh { get; set; }
        private void NotifyPropertyChanged(string info)
        {
            NeedsRefresh = true;
            if (info == "State") NotifyPropertyChanged("StateIcon");
        }

        private bool m_bDownloadActive = false;
        public bool DownloadActive
        {
            get { return m_bDownloadActive; }
            private set
            {
                m_bDownloadActive = value;
                NotifyPropertyChanged("DownloadActive");
                NotifyPropertyChanged("State");
            }
        }

        private string m_Error = "";
        public string Error
        {
            get { return m_Error; }
            set
            {
                m_Error = value;
                Console.Error.WriteLine(value);
                NotifyPropertyChanged("Error");
                NotifyPropertyChanged("State");
            }
        }

        private EpisodeState m_State;
        public EpisodeState State {
            get { return m_State; }
            set
            {
                m_State = value;
                NotifyPropertyChanged("State");
            }
        }

        public Image StateIcon { 
            get {
                if (m_Error != "")
                {
                    return Properties.Resources.bullet_error;
                }
                else
                {
                    switch (State)
                    {
                        case EpisodeState.Queued:
                            return Properties.Resources.bullet_queued;
                        case EpisodeState.Downloading:
                            return Properties.Resources.bullet_downloading;
                        case EpisodeState.Paused:
                            return Properties.Resources.bullet_paused;
                        case EpisodeState.Downloaded:
                            return Properties.Resources.bullet_downloaded;
                        case EpisodeState.Listened:
                            return Properties.Resources.bullet_listened;
                        default:
                            return Properties.Resources.bullet_error;
                    }
                }
            } 
        }

        /// <summary>
        /// Create a new episode item, ready to parse.
        /// </summary>
        private Episode(Feed feed)
        {
            Feed = feed;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="epNode"></param>
        public Episode(Feed feed, XmlNode epNode)
            : this(feed)
        {
            try
            {
                Title = epNode.Attributes["title"].Value;
                MediaURL = epNode.Attributes["url"].Value;
                m_FileInfo = new FileInfo(epNode.Attributes["file"].Value);
                Date = DateTime.FromBinary(long.Parse(epNode.Attributes["date"].Value));
                State = (EpisodeState)Enum.Parse(typeof(EpisodeState), epNode.GetAttributeValue("state", "Queued"));
            }
            catch (Exception e)
            {
                Error = e.Message;
            }

            // New ones aren't downloading!
            if (State == EpisodeState.Downloading)
                State = EpisodeState.Paused;

            CheckFile();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="parent"></param>
        public void SaveToXML(XmlDocument doc, XmlNode parent)
        {
            XmlElement episodeElement = doc.CreateElement("episode");
            episodeElement.SetAttribute("title", Title);
            episodeElement.SetAttribute("url", MediaURL);
            episodeElement.SetAttribute("date", Date.ToBinary().ToString());
            episodeElement.SetAttribute("state", m_State.ToString());
            episodeElement.SetAttribute("file", m_FileInfo.FullName);

            parent.AppendChild(episodeElement);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="itemNode"></param>
        /// <returns></returns>
        public static Episode CreateFromRSS(Feed feed, XmlNode itemNode)
        {
            Episode e = new Episode(feed);
            e.ParseItem(itemNode);

            e.FindFile();

            return e;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="itemNode"></param>
        private void ParseItem(XmlNode itemNode)
        {
            foreach (XmlNode node in itemNode)
            {
                if (node.Name == "title")
                {
                    Title = node.InnerText;
                }
                else if (node.Name.StartsWith("media") || node.Name.StartsWith("enclosure"))
                {
                    MediaURL = node.Attributes["url"].Value;
                }
                else if (node.Name == "pubDate")
                {
                    DateTime date;
                    DateTime.TryParse(node.InnerText, out date);
                    Date = date;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public bool Equals(Episode e)
        {
            return e.MediaURL == MediaURL;
        }

        /// <summary>
        /// 
        /// </summary>
        public float ProgressPercent
        {
            get
            {
                if (ProgressMax > 0)
                    return (1.0f * ProgressCurrent / ProgressMax);
                else
                    return 0.0f;
            }
        }
        public long ProgressMax { get; private set; }
        public long ProgressCurrent { get; private set; }
        private Thread m_DownloadThread = null; 

        /// <summary>
        /// 
        /// </summary>
        public void StartDownload()
        {
            if (State != EpisodeState.Downloading)
            {
                m_DownloadThread = new Thread(Download);
                m_DownloadThread.Start();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void AbortDownload()
        {
            if (m_DownloadThread != null)
            {
                m_DownloadThread.Abort();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Download()
        {
            if (MediaURL != "")
            {
                ProgressCurrent = 0;
                ProgressMax = 1;
                State = EpisodeState.Downloading;
                DownloadActive = true;

                if (m_FileInfo == null)
                    CheckFile();

                FileStream file = m_FileInfo.Open(FileMode.Append, FileAccess.Write);

                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(MediaURL);
                req.AddRange((int)file.Position);

                WebResponse response = req.GetResponse();
                Stream responseStream = response.GetResponseStream();

                ProgressCurrent = file.Position;
                ProgressMax = file.Position + response.ContentLength;

                byte[] buffer = new byte[1024];

                while (DownloadActive)
                {
                    int bytesRead = responseStream.Read(buffer, 0, 1024);
                    ProgressCurrent += bytesRead;
                    if (bytesRead == 0)
                    {
                        DownloadActive = false;
                    }
                    else
                    {
                        file.Write(buffer, 0, bytesRead);
                    }
                }

                responseStream.Close();
                file.Close();

                State = EpisodeState.Downloaded;
                m_DownloadThread = null;
            }
            else
            {
                Error = "Could not determine media URL.";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void FindFile()
        {
            m_FileInfo = new FileInfo(
                Path.Combine(Feed.DirectoryInfo.FullName, PathEx.Sanitize(Title) + ".mp3"));

            CheckFile();
        }

        /// <summary>
        /// 
        /// </summary>
        public void CheckFile()
        {
            if (m_FileInfo.Exists && State == EpisodeState.Queued)
            {
                State = EpisodeState.Paused;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void ShowInExplorer()
        {
            System.Diagnostics.Process.Start("explorer.exe", string.Format("/select,\"{0}\"", m_FileInfo.FullName));
        }
    }
}
